package com.qing.demo.netty.future;

import com.qing.demo.netty.msg.Request;
import com.qing.demo.netty.msg.Response;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;

import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author caozhipeng
 * @date 2025/8/2
 */
public class SyncWrite {

    public Response writeAndSync(final Channel channel, final Request request,final long timeout) throws ExecutionException, InterruptedException, TimeoutException {
        if(channel==null){
            throw new NullPointerException("channel is null");
        }
        if(request==null){
            throw new NullPointerException("request is null");
        }
        if (timeout<=0){
            throw new IllegalArgumentException("timeout must be greater than 0");
        }
        String requestId = UUID.randomUUID().toString();
        request.setRequestId(requestId);

        WriteFuture<Response> writeFuture=new SyncWriteFuture(request.getRequestId());
        SyncWriteMap.syncKey.put(requestId,writeFuture);
        Response response=doWriteAndSync(channel,request,timeout,writeFuture);
        SyncWriteMap.syncKey.remove(requestId);
        return response;
    }

    private Response doWriteAndSync(Channel channel, Request request,long timeout,WriteFuture<Response> writeFuture) throws ExecutionException, InterruptedException, TimeoutException {
        channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                writeFuture.setWriteResult(future.isSuccess());
                writeFuture.setCause(future.cause());
                if(!writeFuture.isWriteSuccess()){
                    SyncWriteMap.syncKey.remove(writeFuture.requestId());
                }
            }
        });

        Response response=writeFuture.get(timeout, TimeUnit.MILLISECONDS);
        if(response==null){
            if(writeFuture.isTimeout()){
                throw new TimeoutException("write timeout");
            }else {
                throw new RuntimeException(writeFuture.cause());
            }
        }
        return response;
    }
}
